#include "Variant.h"
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <iostream>

#define JSON_ASSERT_UNREACHABLE assert(false)

/* Record the length as a prefix.
 */
static inline char *duplicateAndPrefixStringValue(const char *value,
                                                  unsigned int length)
{
    size_t actualLength = sizeof(length) + length + 1;
    auto newString = static_cast<char *>(malloc(actualLength));
    if (newString == nullptr)
    {
        return nullptr;
    }
    *reinterpret_cast<unsigned *>(newString) = length;
    memcpy(newString + sizeof(unsigned), value, length);
    newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later
    return newString;
}

inline static void decodePrefixedString(bool isPrefixed, char const *prefixed,
                                        unsigned *length, char const **value)
{
    if (!isPrefixed)
    {
        *length = static_cast<unsigned>(strlen(prefixed));
        *value = prefixed;
    }
    else
    {
        *length = *reinterpret_cast<unsigned const *>(prefixed);
        *value = prefixed + sizeof(unsigned);
    }
}

static inline void releasePrefixedStringValue(char *value)
{ free(value); }
static inline void releaseStringValue(char *value, unsigned)
{ free(value); }
void markResult(bool result, bool *ok /* = 0*/)
{
    if (nullptr != ok)
    {
        *ok = result;
    }
}

sh::Variant::Variant(sh::ValueType type)
{
    static char const emptyString[] = "";
    initBasic(type);
    switch (type)
    {
        case nullValue:
            break;
        case intValue:
        case uintValue:
            value_.int_ = 0;
            break;
        case realValue:
            value_.real_ = 0.0;
            break;
        case stringValue:
            value_.string_ = const_cast<char *>(static_cast<char const *>(emptyString));
            break;
        case arrayValue:
        case objectValue:
            value_.map_ = new ObjectValues();
            break;
        case booleanValue:
            value_.bool_ = false;
            break;
        default:
            JSON_ASSERT_UNREACHABLE;
    }
}
void sh::Variant::initBasic(sh::ValueType type, bool allocated)
{
    setType(type);
    setIsAllocated(allocated);
}
sh::Variant::Variant(int32_t value)
{
    initBasic(intValue);
    value_.int_ = value;
}
sh::Variant::Variant(uint32_t value)
{
    initBasic(uintValue);
    value_.uint_ = value;
}
sh::Variant::Variant(int64_t value)
{
    initBasic(intValue);
    value_.int_ = value;
}
sh::Variant::Variant(uint64_t value)
{
    initBasic(uintValue);
    value_.uint_ = value;
}
sh::Variant::Variant(double value)
{
    initBasic(realValue);
    value_.real_ = value;
}
sh::Variant::Variant(const char *value)
{
    initBasic(stringValue, true);
    if (nullptr == value)
    {
        return;
    }
    value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(strlen(value)));
}
sh::Variant::Variant(const char *begin, const char *end)
{
    initBasic(stringValue, true);
    value_.string_ = duplicateAndPrefixStringValue(begin, static_cast<unsigned>(end - begin));
}
sh::Variant::Variant(const std::string &value)
{
    initBasic(stringValue, true);
    value_.string_ = duplicateAndPrefixStringValue(value.data(), static_cast<unsigned>(value.length()));
}
sh::Variant::Variant(bool value)
{
    initBasic(booleanValue);
    value_.bool_ = value;
}
sh::Variant::Variant(const std::map<std::string, sh::Variant> &value)
{

}
sh::Variant::Variant(const sh::Variant &other)
{
    dupPayload(other);
    dupMeta(other);
}
void sh::Variant::dupPayload(const sh::Variant &other)
{
    setType(other.type());
    setIsAllocated(false);
    switch (type())
    {
        case nullValue:
        case intValue:
        case uintValue:
        case realValue:
        case booleanValue:
            value_ = other.value_;
            break;
        case stringValue:
            if (other.value_.string_ && other.isAllocated())
            {
                unsigned len;
                char const *str;
                decodePrefixedString(other.isAllocated(), other.value_.string_, &len,
                                     &str);
                value_.string_ = duplicateAndPrefixStringValue(str, len);
                setIsAllocated(true);
            }
            else
            {
                value_.string_ = other.value_.string_;
            }
            break;
        case arrayValue:
        case objectValue:
            value_.map_ = new ObjectValues(*other.value_.map_);
            break;
        default:
            JSON_ASSERT_UNREACHABLE;
    }
}
void sh::Variant::dupMeta(const Variant &other)
{
}
sh::ValueType sh::Variant::type() const
{
    return static_cast<ValueType>(bits_.value_type_);
}
sh::Variant::~Variant()
{
    releasePayload();
    value_.uint_ = 0;
}
void sh::Variant::releasePayload()
{
    switch (type())
    {
        case nullValue:
        case intValue:
        case uintValue:
        case realValue:
        case booleanValue:
            break;
        case stringValue:
            if (isAllocated())
                releasePrefixedStringValue(value_.string_);
            break;
        case arrayValue:
        case objectValue:
            delete value_.map_;
            break;
        default:
            JSON_ASSERT_UNREACHABLE;
    }
}
sh::Variant &sh::Variant::operator=(const sh::Variant &other)
{
    Variant(other).swap(*this);
    return *this;
}
sh::Variant &sh::Variant::operator=(sh::Variant &&other)
{
    other.swap(*this);
    return *this;
}
void sh::Variant::swap(sh::Variant &other)
{
    swapPayload(other);
}
void sh::Variant::swapPayload(sh::Variant &other)
{
    releasePayload();
    dupPayload(other);
}
void sh::Variant::copy(const Variant &other)
{
    copyPayload(other);
    dupMeta(other);
}
void sh::Variant::copyPayload(const sh::Variant &other)
{
    releasePayload();
    dupPayload(other);
}
bool sh::Variant::isNull() const
{
    return type() == nullValue;
}
bool sh::Variant::isBool() const
{
    return type() == booleanValue;
}
bool sh::Variant::isInt() const
{
    return (type() == intValue && value_.int_ <= INT_MAX && value_.int_ >= INT_MIN);
}
bool sh::Variant::isInt64() const
{
    return (type() == intValue && value_.uint_ <= LONG_LONG_MAX && value_.uint_ >= LONG_LONG_MIN);
}
bool sh::Variant::isUInt() const
{
    return (type() == uintValue && value_.uint_ <= UINT_MAX && value_.uint_ >= 0);
}
bool sh::Variant::isUInt64() const
{
    return (type() == uintValue && value_.uint_ <= LONG_LONG_MIN && value_.uint_ >= 0);
}
bool sh::Variant::isIntegral() const
{
    return type() == intValue || type() == uintValue;
}
bool sh::Variant::isDouble() const
{
    return type() == intValue || type() == uintValue || type() == realValue;
}
bool sh::Variant::isNumeric() const
{
    return isDouble();
}
bool sh::Variant::isString() const
{
    return type() == stringValue;
}
bool sh::Variant::isArray() const
{
    return type() == arrayValue;
}
bool sh::Variant::isObject() const
{
    return type() == objectValue;
}

std::string sh::Variant::toString(bool *ok/* =0 */) const
{
    switch (type())
    {
        case nullValue:
            markResult(false, ok);
            return "";
        case stringValue:
        {
            if (value_.string_ == nullptr)
            {
                markResult(false, ok);
                return "";
            }
            unsigned this_len;
            char const *this_str;
            decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, &this_str);
            markResult(true, ok);
            return std::string(this_str, this_len);
        }
        case booleanValue:
            markResult(true, ok);
            return value_.bool_ ? "true" : "false";
        case intValue:
            markResult(true, ok);
            return std::to_string(value_.int_);
        case uintValue:
            markResult(true, ok);
            return std::to_string(value_.uint_);
        case realValue:
            markResult(true, ok);
            return std::to_string(value_.real_);
        default:
            markResult(false, ok);
            return "";
    }
}
int32_t sh::Variant::toInt(bool *ok/* =0 */) const
{
    switch (type())
    {
        case intValue:
            markResult(true, ok);
            return (int32_t) value_.int_;
        case uintValue:
            markResult(true, ok);
            return (int32_t) value_.uint_;
        case realValue:
            markResult(true, ok);
            return (int32_t) value_.real_;
        case nullValue:
            markResult(false, ok);
            return 0;
        case booleanValue:
            markResult(true, ok);
            return value_.bool_ ? 1 : 0;
        default:
            markResult(false, ok);
            return 0;
    }
}
uint32_t sh::Variant::toUInt(bool *ok/* =0 */) const
{
    switch (type())
    {
        case intValue:
            markResult(true, ok);
            return (uint32_t) value_.int_;
        case uintValue:
            markResult(true, ok);
            return (uint32_t) value_.uint_;
        case realValue:
            markResult(true, ok);
            return (uint32_t) value_.real_;
        case nullValue:
            markResult(false, ok);
            return 0;
        case booleanValue:
            markResult(true, ok);
            return value_.bool_ ? 1 : 0;
        default:
            markResult(false, ok);
            return 0;
    }
}
int64_t sh::Variant::toInt64(bool *ok/* =0 */) const
{
    switch (type())
    {
        case intValue:
            markResult(true, ok);
            return (int64_t) value_.int_;
        case uintValue:
            markResult(true, ok);
            return (int64_t) value_.uint_;
        case realValue:
            markResult(true, ok);
            return (int64_t) value_.real_;
        case nullValue:
            markResult(false, ok);
            return 0;
        case booleanValue:
            markResult(true, ok);
            return value_.bool_ ? 1 : 0;
        default:
            markResult(false, ok);
            return 0;
    }
}
uint64_t sh::Variant::toUInt64(bool *ok/* =0 */) const
{
    switch (type())
    {
        case intValue:
            markResult(true, ok);
            return (uint64_t) value_.int_;
        case uintValue:
            markResult(true, ok);
            return (uint64_t) value_.uint_;
        case realValue:
            markResult(true, ok);
            return (uint64_t) value_.real_;
        case nullValue:
            markResult(false, ok);
            return 0;
        case booleanValue:
            markResult(true, ok);
            return value_.bool_ ? 1 : 0;
        default:
            markResult(false, ok);
            return 0;
    }
}
float sh::Variant::toFloat(bool *ok/* =0 */) const
{
    switch (type())
    {
        case intValue:
            markResult(true, ok);
            return static_cast<float>(value_.int_);
        case uintValue:
            markResult(true, ok);
            return static_cast<float>(value_.uint_);
        case realValue:
            markResult(true, ok);
            return static_cast<float>(value_.real_);
        case nullValue:
            markResult(false, ok);
            return 0.0F;
        case booleanValue:
            markResult(true, ok);
            return value_.bool_ ? 1.0F : 0.0F;
        default:
            markResult(false, ok);
            return 0.0F;
    }
}
double sh::Variant::toDouble(bool *ok/* =0 */) const
{
    switch (type())
    {
        case intValue:
            markResult(true, ok);
            return static_cast<double>(value_.int_);
        case uintValue:
            markResult(true, ok);
            return static_cast<double>(value_.uint_);
        case realValue:
            markResult(true, ok);
            return value_.real_;
        case nullValue:
            markResult(false, ok);
            return 0.0;
        case booleanValue:
            markResult(true, ok);
            return value_.bool_ ? 1.0 : 0.0;
        default:
            markResult(false, ok);
            return 0.0F;
    }
}
bool sh::Variant::toBool(bool *ok/* =0 */) const
{
    switch (type())
    {
        case booleanValue:
            markResult(true, ok);
            return value_.bool_;
        case nullValue:
            markResult(false, ok);
            return false;
        case intValue:
            markResult(true, ok);
            return value_.int_ != 0;
        case uintValue:
            markResult(true, ok);
            return value_.uint_ != 0;
        case realValue:
        {
            markResult(true, ok);
            // According to JavaScript language zero or NaN is regarded as false
            const auto value_classification = std::fpclassify(value_.real_);
            return value_classification != FP_ZERO && value_classification != FP_NAN;
        }
        default:
            markResult(false, ok);
            return false;
    }
}
uint32_t sh::Variant::size() const
{
    switch (type())
    {
        case nullValue:
        case intValue:
        case uintValue:
        case realValue:
        case booleanValue:
        case stringValue:
            return 0;
        case arrayValue:
        case objectValue:
            return value_.map_->size();
    }
    JSON_ASSERT_UNREACHABLE;
    return 0; // unreachable;
}
bool sh::Variant::empty() const
{
    if (isNull() || isArray() || isObject())
        return size() == 0U;
    return false;
}
sh::Variant::operator bool() const
{
    return !isNull();
}
void sh::Variant::clear()
{
    switch (type())
    {
        case arrayValue:
        case objectValue:
            value_.map_->clear();
            break;
        default:
            break;
    }
}
